什么是 JWT
JSON Web Token (JWT) 是一个开放标准(RFC 7519) ,定义了一种紧凑且自包含(免存储)的方式,以JSON对象的形式在各方之间安全地传输信息。你可以验证和信任该信息,因为它经过了数字签名。JWTs可以使用密钥(HMAC)、RSA、ECDSA 公钥/私钥对其签名。
如何使用
安装
yarn add jose
生成
const {JWT, JWK} = require('jose')
const key = JWK.asKey({
kty: 'oct',
// 自定义密钥
k: 'k04zAv2i134GU6W8mhGFqnftI1lLXRY-Sl7oV0PNy35',
})
const jwt = JWT.sign({
// 可在任何地方解密的明文信息
timestamp: new Date(),
}, key, {
// 颁发给谁
audience: ['urn:web:client'],
// 由谁颁发
issuer: 'https://stdout.com.com',
// 过期时间,2 min 两分钟,2s 两秒,可自定义
expiresIn: '2 hours',
// 头信息
header: {
typ: 'JWT',
}
})
// eyJ0eXAiOiJKV13iLCJhbGciOiJIUzI1NiJ9.eyJ0aW1lc3RhbXAiOiIyMDIwLTA2LTE1VDA4OjQxOjIxLjE5MFoiLCJhdWQiOlsidXJuOndlYjpjbGllbnQiXSwiaXNzIjoiaHR0cHM6Ly9wcmludGxuLm9yZyIsImlhdCI6MT15MjIxMDQ4MSwiZXhwIjoxNTkyMjE3NjgxfQ.A_jBEcn7aC6-9l717WCktXDxPqS5epjXF3SVEG4GjPQ
验证
const {JWT, JWK} = require('jose')
const key = JWK.asKey({
kty: 'oct',
// 自定义密钥,必须与生成时密钥保持一致
k: 'k04zAv2i134GU6W8mhGFqnftI1lLXRY-Sl7oV0PNy35',
})
try {
const info = JWT.verify(token, key, {
// 务必与颁发时保持一致
audience: 'urn:web:client',
issuer: 'https://stdout.com.com',
clockTolerance: '2 hours',
})
} catch (e) {
// 验证错误
// e.code === 'ERR_JWT_EXPIRED' 时,为token过期,可用于重新颁发 token
// 其他 code 详见文档
}
解析
const {JWT, JWK} = require('jose')
const key = JWK.asKey({
kty: 'oct',
// 自定义密钥,必须与生成时密钥保持一致
k: 'k04zAv2i134GU6W8mhGFqnftI1lLXRY-Sl7oV0PNy35',
})
const detail = JWT.decode(jwt, key)
// detail
{
"timestamp"
:
"2020-06-15T08:54:22.086Z",
"aud"
:
[
"urn:web:client"
],
"iss"
:
"https://stdout.com.com",
"iat"
:
1592211262,
"exp"
:
1592218462
}
接入 egg.js
Egg.js 大致用法参考使用 egg.js 为 hexo 搭建后端,实现短链接服务
封装
app/utils/jwt.js
const {JWT, JWK} = require('jose')
// 验证 token
module.exports.verifyToken = token => JWT.verify(token, this.asKey(), {
audience: 'urn:web:client',
issuer: 'https://stdout.com.com',
clockTolerance: '2 hours',
})
// 生成token
module.exports.asKey = () => JWK.asKey({
kty: 'oct',
k: 'k04zAv2i134GU6W8mhGFqnftI1lLXRY-Sl7oV0PNy35',
})
module.exports.makeToken = () => {
return JWT.sign({
timestamp: new Date(),
}, this.asKey(), {
audience: ['urn:web:client'],
issuer: 'https://stdout.com.com',
expiresIn: '2 hours',
header: {
typ: 'JWT',
},
})
}
module.exports.decode = jwt => {
return JWT.decode(jwt, this.asKey())
}
控制器
const {makeToken, verifyToken} = require('../utils/jwt')
const Controller = require('egg').Controller
class AuthController extends Controller {
// 生成 jwt-token
makeToken() {
// 可选操作,登陆注册等前置操作
let token = makeToken()
this.ctx.body = {
token,
}
}
// 验证token
checkToken() {
if (!this.ctx.request.body.token) {
return this.ctx.body = {
message: 'token is required',
}
}
try {
// 获取信息,返回值与 decode 一致
const client = verifyToken(this.ctx.request.body.token)
} catch (e) {
if (e.code === 'ERR_JWT_EXPIRED') {
// 可选操作,刷新 token
} else {
return this.ctx.body = {
message: e.message,
}
}
}
// 业务逻辑
}
}
module.exports = AuthController
路由
router.get('/api/secret/make-token', controller.auth.makeToken)
router.get('/api/secret/check-token', controller.auth.checkToken)
尝试
$ curl -L 'https://api.stdout.com.com/api/secret/make-token'
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0aW1lc3RhbXAiOiIyMDIwLTA2LTE1VDA5OjA4OjA5LjU4NFoiLCJhdWQiOlsidXJuOndlYjpjbGllbnQiXSwiaXNzIjoiaHR0cHM6Ly9wcmludGxuLm9yZyIsImlhdCI6MTU5MjIxMjA4OSwiZXhwIjoxNTkyMjE5Mjg5fQ.WPA46o4rXlAgktIi-ktKA8I351tyL49Z2qdnGeGdALU"}